<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>cannon.js - shapes demo</title>
    <link rel="stylesheet" href="css/style.css" type="text/css" />
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
  </head>
  <body>
    <script type="module">
      import * as CANNON from '../dist/cannon-es.js'
      import { Demo } from './js/Demo.js'

      const demo = new Demo()

      /**
       * Showcase all available shapes.
       */

      function createTetra() {
        const vertices = [
          new CANNON.Vec3(0, 0, 0),
          new CANNON.Vec3(2, 0, 0),
          new CANNON.Vec3(0, 2, 0),
          new CANNON.Vec3(0, 0, 2),
        ]
        const offset = -0.35
        for (let i = 0; i < vertices.length; i++) {
          const v = vertices[i]
          v.x += offset
          v.y += offset
          v.z += offset
        }
        return new CANNON.ConvexPolyhedron({
          vertices,
          faces: [
            [0, 3, 2], // -x
            [0, 1, 3], // -y
            [0, 2, 1], // -z
            [1, 2, 3], // +xyz
          ],
        })
      }

      demo.addScene('All shapes', () => {
        const world = setupWorld(demo)

        const mass = 1
        const size = 1

        // Sphere shape
        const sphereShape = new CANNON.Sphere(size)
        const sphereBody = new CANNON.Body({ mass })
        sphereBody.addShape(sphereShape)
        sphereBody.position.set(-size * 2, size + 1, size * 2)
        world.addBody(sphereBody)
        demo.addVisual(sphereBody)

        // Cylinder shape
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const cylinderBody = new CANNON.Body({ mass })
        cylinderBody.addShape(cylinderShape)
        cylinderBody.position.set(size * 2, size + 1, size * 2)
        world.addBody(cylinderBody)
        demo.addVisual(cylinderBody)

        // Cylinder shape 2
        const cylinderShape2 = new CANNON.Cylinder(size, size, size * 2, 10)
        const cylinderBody2 = new CANNON.Body({ mass })
        cylinderBody2.addShape(cylinderShape2)
        cylinderBody2.position.set(size * 2, size * 4 + 1, size * 2)
        cylinderBody2.quaternion.setFromEuler(Math.PI / 2, Math.PI / 2, 0)
        world.addBody(cylinderBody2)
        demo.addVisual(cylinderBody2)

        // Box shape
        const boxShape = new CANNON.Box(new CANNON.Vec3(size, size, size))
        const boxBody = new CANNON.Body({ mass })
        boxBody.addShape(boxShape)
        boxBody.position.set(size * 2, size + 1, -size * 2)
        world.addBody(boxBody)
        demo.addVisual(boxBody)

        // Particle - not a shape but still here to show how to use it.
        const particle = new CANNON.Body({ mass })
        particle.addShape(new CANNON.Particle())
        particle.position.set(size * 2, size + 1, size * 4)
        world.addBody(particle)
        demo.addVisual(particle)

        // Compound
        const compoundBody = new CANNON.Body({ mass })
        const shape = new CANNON.Box(new CANNON.Vec3(size * 0.5, size * 0.5, size * 0.5))
        compoundBody.addShape(shape, new CANNON.Vec3(0, size, 0))
        compoundBody.addShape(shape, new CANNON.Vec3(0, 0, 0))
        compoundBody.addShape(shape, new CANNON.Vec3(0, -size, 0))
        compoundBody.addShape(shape, new CANNON.Vec3(size, -size, 0))
        compoundBody.position.set(size * 4, size + 1, size * 4)
        world.addBody(compoundBody)
        demo.addVisual(compoundBody)

        // ConvexPolyhedron tetra shape
        const polyhedronShape = createTetra()
        const polyhedronBody = new CANNON.Body({ mass })
        polyhedronBody.addShape(polyhedronShape)
        polyhedronBody.position.set(-size * 2, size + 1, -size * 2)
        world.addBody(polyhedronBody)
        demo.addVisual(polyhedronBody)
      })

      demo.start()

      function setupWorld(demo) {
        const world = demo.getWorld()
        world.gravity.set(0, -30, 0)

        // Max solver iterations: Use more for better force propagation, but keep in mind that it's not very computationally cheap!
        world.solver.iterations = 17

        // Tweak contact properties.
        // Contact stiffness - use to make softer/harder contacts
        world.defaultContactMaterial.contactEquationStiffness = 1e6

        // Stabilization time in number of timesteps
        world.defaultContactMaterial.contactEquationRelaxation = 3

        // Static ground plane
        const groundShape = new CANNON.Plane()
        const groundBody = new CANNON.Body({ mass: 0 })
        groundBody.addShape(groundShape)
        groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
        world.addBody(groundBody)
        demo.addVisual(groundBody)

        return world
      }
    </script>
  </body>
</html>
